
package com.bolt.support.dynamic.datasource.autoconfigure;


import com.bolt.support.dynamic.datasource.DynamicRoutingDataSource;
import com.bolt.support.dynamic.datasource.annotation.DataSourceName;
import com.bolt.support.dynamic.datasource.annotation.ExtTransactional;
import com.bolt.support.dynamic.datasource.aop.DynamicDataSourceAnnotationAdvisor;
import com.bolt.support.dynamic.datasource.aop.DynamicDataSourceAnnotationInterceptor;
import com.bolt.support.dynamic.datasource.aop.DynamicLocalTransactionInterceptor;
import com.bolt.support.dynamic.datasource.autoconfigure.druid.DruidDynamicDataSourceConfiguration;
import com.bolt.support.dynamic.datasource.event.DataSourceInitEvent;
import com.bolt.support.dynamic.datasource.event.EncDataSourceInitEvent;
import com.bolt.support.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.bolt.support.dynamic.datasource.provider.YmlDynamicDataSourceProvider;
import com.bolt.support.dynamic.datasource.toolkit.DialectContextHolder;
import org.springframework.aop.Advisor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.*;
import org.springframework.core.annotation.Order;

import javax.sql.DataSource;

/**
 * 动态数据源核心自动配置类
 */
@Configuration
@EnableConfigurationProperties({DynamicDataSourceProperties.class})
@AutoConfigureBefore(value = DataSourceAutoConfiguration.class, name = "com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure")
@Import(value = {DruidDynamicDataSourceConfiguration.class, DynamicDataSourceCreatorAutoConfiguration.class})
@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX, name = "enabled", havingValue = "true")
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
public class DynamicDataSourceAutoConfiguration {

    private final DynamicDataSourceProperties properties;


    public DynamicDataSourceAutoConfiguration(DynamicDataSourceProperties properties) {
        this.properties = properties;
        DialectContextHolder.initialize(this.properties);
    }

    @Bean
    @Order(0)
    public DynamicDataSourceProvider ymlDynamicDataSourceProvider() {
        return new YmlDynamicDataSourceProvider(properties.getDatasource());
    }

    @Bean
    @ConditionalOnMissingBean
    public DataSource dataSource() {
        DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
        dataSource.setPrimary(properties.getPrimary());
        dataSource.setStrict(properties.getStrict());
        dataSource.setStrategy(properties.getStrategy());
        dataSource.setSeata(properties.getSeata());
        return dataSource;
    }



    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    @Bean
    @ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX + ".aop", name = "enabled", havingValue = "true", matchIfMissing = true)
    public Advisor dynamicDatasourceAnnotationAdvisor() {
        DynamicDatasourceAopProperties aopProperties = properties.getAop();
        DynamicDataSourceAnnotationInterceptor interceptor = new DynamicDataSourceAnnotationInterceptor(aopProperties.getAllowedPublicOnly());
        DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(interceptor, DataSourceName.class);
        advisor.setOrder(aopProperties.getOrder());
        return advisor;
    }

    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    @Bean
    @ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX, name = "seata", havingValue = "false", matchIfMissing = true)
    public Advisor dynamicTransactionAdvisor() {
        DynamicLocalTransactionInterceptor interceptor = new DynamicLocalTransactionInterceptor();
        return new DynamicDataSourceAnnotationAdvisor(interceptor, ExtTransactional.class);
    }

    @Bean
    @ConditionalOnMissingBean
    public DataSourceInitEvent dataSourceInitEvent() {
        return new EncDataSourceInitEvent();
    }

//    @Primary
//    @Bean(name = "entityManagerPrimary")
//    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(
//            MultiTenantConnectionProvider multiTenantConnectionProvider,
//            CurrentTenantIdentifierResolver currentTenantIdentifierResolver) {
//
//        Map<String, Object> hibernateProperties = new LinkedHashMap<>();
//        hibernateProperties.put(Environment.SHOW_SQL, true);
//        hibernateProperties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
//        hibernateProperties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProvider);
//        hibernateProperties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver);
//        hibernateProperties.put(Environment.DIALECT, "com.bolt.support.dynamic.datasource.hibernate.MultiDataSourceDialect");
//        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
//
//        entityManagerFactoryBean.setPackagesToScan("com.bolt.**.entity");
//        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
//        entityManagerFactoryBean.setJpaPropertyMap(hibernateProperties);
//        return entityManagerFactoryBean;
//    }


//    @Primary
//    @Bean
//    public EntityManager entityManagerFactory(@Qualifier("entityManagerPrimary") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
//        return entityManagerFactoryBean.getObject().createEntityManager();
//    }


//    @Bean
//    public PlatformTransactionManager multiTenantTxManager(EntityManagerFactory entityManagerFactory) {
//        JpaTransactionManager transactionManager = new JpaTransactionManager();
//        transactionManager.setEntityManagerFactory(entityManagerFactory);
//        return transactionManager;
//    }


}
